package com.tax.infrastructure.extension;

import com.tax.config.exception.BusinessException;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Objects;

@Component
@Log4j2
public class ExtensionExecutor extends AbstractExtensionComponentExecutor {

    public static final String DOT_SEPARATOR = ".";

    @Autowired
    private ExtensionRepository extensionRepository;

    @Override
    public <C> C locateComponent(Class<C> targetClz, BizScenario bizScenario) {
        C extension=locateExtension(targetClz,bizScenario);
        log.debug("[Located Extension]:"+extension.getClass().getSimpleName());
        return extension;
    }

    protected  <Ext> Ext locateExtension(Class<Ext> targetClz, BizScenario bizScenario) {
        checkNull(bizScenario);
        String uniqueIdentity = bizScenario.getUniqueIdentity();
        log.debug("BizScenario in locateExtension is : " + uniqueIdentity);

        Ext extension=firstTry(targetClz,uniqueIdentity);
        if (!Objects.isNull(extension)){
            return extension;
        }
        extension=loopTry(targetClz,uniqueIdentity);
        if (!Objects.isNull(extension)){
            return extension;
        }
        extension=firstTry(targetClz,BizScenario.getDefaultUniqueIdentity());
        if (!Objects.isNull(extension)){
            return extension;
        }
        throw new BusinessException("can not find extension with ExtensionPoint: "+targetClz+" BizSenario: "+uniqueIdentity);
    }

    private <Ext> Ext loopTry(Class<Ext> targetClz, String bizScenario) {
        if (Objects.isNull(bizScenario)){
            return null;
        }
        Ext extension;
        int lastDotIndex = bizScenario.lastIndexOf(DOT_SEPARATOR);
        while(lastDotIndex != -1){
            bizScenario = bizScenario.substring(0,lastDotIndex);
            extension = (Ext) extensionRepository.get(new ExtensionCoordinate(targetClz.getName(),bizScenario));
            if (null!=extension){
                return extension;
            }
            lastDotIndex = bizScenario.lastIndexOf(DOT_SEPARATOR);
        }
        return null;
    }

    private <Ext> Ext firstTry(Class<Ext> targetClz, String bizScenario) {
        return (Ext) extensionRepository.get(new ExtensionCoordinate(targetClz.getName(),bizScenario));
    }

    private void checkNull(BizScenario bizScenario) {
        if (Objects.isNull(bizScenario)){
            throw new BusinessException("BizScenario can not be null for extension");
        }
    }
}
